package com.fyt.reachgo.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fyt.reachgo.config.RabbitMQConfig;
import com.fyt.reachgo.domain.Result;
import com.fyt.reachgo.domain.UserFavorite;
import com.fyt.reachgo.domain.VO.UserFavoriteVO;
import com.fyt.reachgo.service.UserFavoriteService;
import com.fyt.reachgo.mapper.UserFavoriteMapper;
import com.fyt.reachgo.utils.RedisConstants;
import com.fyt.reachgo.utils.UserHolder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author 27675
* @description 针对表【user_favorite(用户收藏行为记录表)】的数据库操作Service实现
* @createDate 2025-02-25 08:37:04
*/
@Service
@Slf4j
@RequiredArgsConstructor
public class UserFavoriteServiceImpl extends ServiceImpl<UserFavoriteMapper, UserFavorite>
        implements UserFavoriteService {

    private final StringRedisTemplate redisTemplate;
    private final RabbitTemplate rabbitTemplate;
    private final UserFavoriteMapper userFavoriteMapper;
    private final RedissonClient redissonClient;

    // Redis key前缀
    private static final String FAVORITE_KEY_PREFIX = "user_favorite:";
    private static final String USER_FAVORITES_SET_KEY = "user_favorites_set:";
    public static final String USER_FAVORITES_LIST_KEY = "user_favorites_list:";
    // 分布式锁key前缀
    private static final String LOCK_KEY_PREFIX = "lock:favorite:";
    // 锁过期时间
    private static final long LOCK_EXPIRE_TIME = 30; // 秒

    @Override
    public Result addCollect(UserFavorite userFavorite) {
        Long userId = UserHolder.getUser().getUserId();
        Integer targetId = userFavorite.getTargetId();
        String targetType = "guide"; // 固定为guide

        // 构造Redis key
        String favoriteKey = buildFavoriteKey(userId, targetType, targetId);
        String lockKey = LOCK_KEY_PREFIX + userId + ":" + targetType + ":" + targetId;
        String userFavoritesSetKey = USER_FAVORITES_SET_KEY + userId;

        // 1. 先检查Redis中是否已存在
        if (Boolean.TRUE.equals(redisTemplate.hasKey(favoriteKey))) {
            return Result.error("已经收藏过啦");
        }

        RLock rLock = redissonClient.getLock(lockKey);
        try {
            // 尝试获取锁，设置合理的等待时间
            boolean isLocked = rLock.tryLock(LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
            if (isLocked) {
                try {
                    // 双重检查
                    if (Boolean.TRUE.equals(redisTemplate.hasKey(favoriteKey))) {
                        return Result.error("已经收藏过啦");
                    }

                    // 设置收藏信息
                    userFavorite.setUserId(userId);
                    userFavorite.setCreateTime(LocalDateTime.now());
                    userFavorite.setTargetType(targetType);

                    // 将收藏数据存入Redis Hash结构
                    redisTemplate.opsForHash().put(favoriteKey, "data", JSONUtil.toJsonStr(userFavorite));
//                    // 设置过期时间
//                    redisTemplate.expire(favoriteKey, 30, TimeUnit.DAYS);

                    // 将收藏key添加到用户收藏集合中
                    redisTemplate.opsForSet().add(userFavoritesSetKey, favoriteKey);

                    // 发送消息到RabbitMQ异步处理数据库写入
                    rabbitTemplate.convertAndSend(
                            RabbitMQConfig.USER_FAVORITE_EXCHANGE,
                            RabbitMQConfig.USER_FAVORITE_ROUTING_KEY,
                            userFavorite);

                    log.info("收藏成功，已写入Redis并发送异步消息");
                    return Result.success(userFavorite); // 返回添加的收藏数据
                } finally {
                    rLock.unlock();
                }
            }
            return Result.error("操作频繁，请稍后重试");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("获取分布式锁时被中断", e);
            return Result.error("系统繁忙，请稍后重试");
        }
    }


    @Override
    public Result getCollects() {
        Long userId = UserHolder.getUser().getUserId();
        // 查询缓存
        String userFavoritesKey = USER_FAVORITES_LIST_KEY + userId;
        // 如果缓存存在
        if (Boolean.TRUE.equals(redisTemplate.hasKey(userFavoritesKey))) {
            log.debug("缓存命中：{}", userFavoritesKey);
            // 解决缓存穿透
            String json = redisTemplate.opsForValue().get(userFavoritesKey);
            if (StrUtil.isBlank(json)) {
                // 解决缓存穿透
                return Result.error("用户未收藏任何内容");
            }
            return Result.success(JSONUtil.toBean(json, Map.class));
        }
        // 如果缓存不存在，查询数据库
        List<UserFavoriteVO> userFavorites = userFavoriteMapper.selectUserFavorites(userId);
        if (userFavorites.isEmpty()) {
            // 解决缓存穿透
            redisTemplate.opsForValue().set(userFavoritesKey, "",
                    RedisConstants.CACHE_NULL_TTL,
                    TimeUnit.MINUTES);
            return Result.error("用户未收藏任何内容");
        }
        // 将数据按照targetType进行分组
        Map<String, List<UserFavoriteVO>> groupedFavorites = userFavorites.stream()
                .collect(Collectors.groupingBy(UserFavoriteVO::getTargetType));
        // 存入redis
        redisTemplate.opsForValue().set(userFavoritesKey, JSONUtil.toJsonStr(groupedFavorites),
                RedisConstants.CACHE_COLLECT_TTL + RandomUtil.randomLong(20),
                TimeUnit.MINUTES);
        return Result.success(groupedFavorites);
    }

    // 构建Redis key
    private String buildFavoriteKey(Long userId, String targetType, Integer targetId) {
        return FAVORITE_KEY_PREFIX + userId + ":" + targetType + ":" + targetId;
    }

    // 检查数据库中是否已存在
    private boolean checkExistsInDB(Long userId, String targetType, Integer targetId) {
        QueryWrapper<UserFavorite> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId)
                .eq("target_id", targetId)
                .eq("target_type", targetType);
        return userFavoriteMapper.selectCount(queryWrapper) > 0;
    }

}




